import qs from 'qs';
import { erroToText, toLabel, valueType, getFormData } from './config';

// 初始化配置
interface IRequestConfig {
  host: string; // API地址
  apiPath: string; // API目录
  photoPath: string; // 访问图片目录
  timeout: number; // 超时时间
  defaultConfig: any; // 默认配置
  interceptorsRequest: (url: string, config: any) => any; // 请求拦截
  interceptorsResponse: (url: string, config: any, res: any) => any; // 响应拦截
}

interface IConfig {
  label: string; // 日志类型
  method?: string; // 请求类型
  timeout?: number; // 请求超时
  [key: string]: any;
}

/**
 * 请求器
 * @param  {string}  url    请求地址
 * @param  {any}     data   请求数据
 * @param  {any}     config 请求配置
 * @return {Promise}        返回处理后的 Promise
 */
type TRequest = (url: string, data?: any, config?: IConfig | string) => Promise<any>;

// 初始化请求模型
export default (config: IRequestConfig) => {
  const { host, apiPath, photoPath, timeout, defaultConfig, interceptorsRequest, interceptorsResponse } = config;
  const baseURL = host + apiPath; // api地址前缀
  const photo = host + photoPath; // 图片访问地址前缀

  /**
   * 执行请求
   * @param  {string}  url    请求地址
   * @param  {any}     config 请求配置
   * @return {Promise}        返回处理后的 Promise
   */
  const request = (url: string, config?: any) => {
    // 拼接地址
    if (!/^http/.test(url)) url = baseURL + url;

    // 合并配置并执行请求拦截
    config = interceptorsRequest(url, {
      ...defaultConfig,
      ...config,
    });

    // 发出请求
    return Promise.race([
      // 追加请求超时
      fetch(url, config),
      new Promise((resolve, reject) => {
        setTimeout(() => reject('request timeout'), config.timeout || timeout);
      }),
    ])
      .then(response => {
        // 转化响应数据
        return response[config.dataFormat]().then((res: any) => {
          if (valueType.indexOf(typeof res) > -1) {
            return Promise.reject('No JSON');
          }
          return res;
        });
      })
      .catch(error => {
        // 错误类型检查
        return Promise.resolve({
          error,
          errorText: erroToText(error),
        });
      })
      .then(res => interceptorsResponse(url, config, res)); // 载入响应拦截
  };

  const get: TRequest = (address, data, config) => {
    const data_ = qs.stringify(data);
    return request(data_ ? `${address}?${data_}` : address, {
      data,
      address,
      ...toLabel(config),
    });
  };

  const post: TRequest = (address, data, config) => {
    return request(address, {
      method: 'post',
      data,
      address,
      ...toLabel(config),
    });
  };

  const put: TRequest = (address, data, config) => {
    return request(address, {
      method: 'put',
      data,
      address,
      ...toLabel(config),
    });
  };

  const del: TRequest = (address, data, config) => {
    return request(address, {
      method: 'delete',
      data,
      address,
      ...toLabel(config),
    });
  };

  const upload: TRequest = (address, data, config) => {
    return request(address, {
      method: 'post',
      headers: {},
      address,
      body: getFormData(data),
      data,
      ...toLabel(config),
    });
  };

  return {
    baseURL,
    photo,
    request,
    get,
    post,
    put,
    del,
    upload,
  };
};
